using System;
using System.Collections.Generic;
using System.Text;

namespace Util
{
	public static class ParsingUtil
	{
		public static bool TryRead(StreamEx stream, string value)
		{
			int startPos = stream.Position;
			foreach (char pattern in value)
			{
				char ch = stream.Read();
				if (ch != pattern)
				{
					stream.Seek(startPos);
					return false;
				}
			}
			return true;
		}

		public delegate void SkipDelegate(StreamEx stream);

		public static string Capture(StreamEx stream, SkipDelegate skipper)
		{
			int start = stream.Position;
			skipper(stream);
			int count = stream.Position - start;
			stream.Seek(start);
			StringBuilder res = new StringBuilder();
			foreach (char ch in stream.Read(count))
			{
				res.Append(ch);
			}
			return res.ToString();
		}

		public static string ReadSpaces(StreamEx stream)
		{
			return Capture(stream, SkipSpaces);
		}

		public static string ReadLineComment(StreamEx stream)
		{
			return Capture(stream, SkipLineComment);
		}

		public static string ReadComment(StreamEx stream)
		{
			return Capture(stream, SkipComment);
		}

		public static string ReadToLineEnd(StreamEx s)
		{
			StringBuilder res = new StringBuilder();
			bool endLineReached = false;
			while (!s.Depleted)
			{
				char ch = s.Read();
				if (ch == '\r' || ch == '\n')
				{
					endLineReached = true;
				}
				else
				{
					if (endLineReached)
					{
						s.StepBack();
						break;
					}

					res.Append(ch);
				}
			}
			return res.ToString();
		}

		public static bool ReadDelimiter(StreamEx s, char pattern)
		{
			int startPos = s.Position;
			string tmp = ReadToLineEnd(s);
			if (StringUtil.Exists(tmp, delegate(char ch) { return ch != pattern; }))
			{
				s.Position = startPos;
				return false;
			}
			else
				return true;
		}

		public static void SkipSpaces(StreamEx stream)
		{
			while (true)
			{
				if (stream.Depleted)
					return;

				char ch = stream.Read();
				if (ch != ' ' && ch != '\t' && ch != '\r' && ch != '\n')
				{
					stream.StepBack();
					return;
				}
			}
		}

		public static void SkipLineComment(StreamEx stream)
		{
			char ch = stream.Read();
			if (ch != '/')
			{
				stream.StepBack();
				return;
			}

			ch = stream.Read();
			if (ch == '/')
			{
				SkipToLineEnd(stream);
			}
			else
			{
				stream.StepBack();
				stream.StepBack();
				return;
			}
		}

		public static void SkipComment(StreamEx stream)
		{
			char ch = stream.Read();
			if (ch != '/')
			{
				stream.StepBack();
				return;
			}

			ch = stream.Read();
			if (ch == '*')
			{
				SkipToIncluding(stream, "*/");
			}
			else
			{
				stream.StepBack();
				stream.StepBack();
				return;
			}
		}

		public static void SkipSpacesAndComments(StreamEx stream)
		{
			while (true)
			{
				SkipSpaces(stream);
				if (stream.Depleted)
					return;
				char ch = stream.Read();
				if (ch != '/')
				{
					stream.StepBack();
					return;
				}

				ch = stream.Read();
				if (ch == '*')
				{
					SkipToIncluding(stream, "*/");
				}
				else if (ch == '/')
				{
					SkipToLineEnd(stream);
				}
				else
				{
					stream.StepBack();
					stream.StepBack();
					return;
				}
			}
		}

		public static void SkipToLineEnd(StreamEx stream)
		{
			while (!stream.Depleted)
			{
				char ch = stream.Read();
				if (ch == '\r' || ch == '\n')
				{
					stream.StepBack();
					return;
				}
			}
		}

		public static void SkipToIncluding(StreamEx stream, string mark)
		{
			while (true)
			{
				bool found = true;
				int startPos = stream.Position;
				for (int i = 0; i < mark.Length; i++)
				{
					char ch = stream.Read();
					if (ch != mark[i])
					{
						found = false;
						break;
					}
				}
				if (found)
					break;
				else
					stream.Position = startPos + 1;
			}
		}

		public static void SkipTo(StreamEx stream, string mark)
		{
			SkipToIncluding(stream, mark);
			stream.Seek(stream.Position - mark.Length);
		}
	}
}
